﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

namespace Soft
{

    public class LogFile
    {


        /// <summary>
        /// 日志文件
        /// </summary>
        /// <param name="fileName">文件名称</param>
        /// <param name="cycleTime">循环时间</param>
        /// <param name="maxLength">最大长度</param>
        /// <param name="subPath">子路径</param>
        /// <param name="encoding">编码格式</param>
        public LogFile(string fileName, TimeUnit cycleTime, ushort maxLength, string subPath, Encoding encoding)
        {
            if (!fileName.CheckFileName(false))
            {
                throw new Exception("The file name cannot contain the following characters: \\, /, :, *, ?, <, >, |");
            }
            if (cycleTime < TimeUnit.Hour)
            {
                throw new Exception("The record cycle of the log file cannot be less than hour.");
            }
            if (!subPath.CheckFileName(false))
            {
                throw new Exception("The subpath name cannot contain the following characters: \\, /, :, *, ?, <, >, |");
            }
            this._FixFileName = fileName;
            this.CycleTime = cycleTime;
            this.SubPath = subPath;
            this.MaxFileSize = (long)((int)maxLength * 1048576);
            this.Encoding = encoding;
            this.Path = FactoryAdvisorPlatformGlobalSet.ApplicationPath;
            if (!string.IsNullOrEmpty(subPath))
            {
                this.Path = this.Path + subPath + "\\";
            }
            this._Timer = new System.Timers.Timer((double)(this._AutoSaveSeconds * 1000));
            this._Timer.Elapsed += this._Timer_Elapsed;
            this._Timer.Start();
        }


        /// <summary>
        /// 日志文件
        /// </summary>
        /// <param name="fileName">文件名称</param>
        /// <param name="cycleTime">循环时间</param>
        /// <param name="maxLength">最大长度</param>
        public LogFile(string fileName, TimeUnit cycleTime, ushort maxLength) : this(fileName, cycleTime, maxLength, "Log", Encoding.Unicode)
        {
        }
       

        /// <summary>
        /// 析构函数
        /// </summary>
        ~LogFile()
        {
            this._Timer.Stop();
            this.IgnoreErrors = true;
            this.WriteFile(false);
            if (this._Task != null)
            {
                this._Task.Wait();
            }
        }
  

        /// <summary>
        /// 文件的全路径+名称
        /// </summary>
        public string File
        {
            get
            {
                return string.Concat(new string[]
                {
                    this.Path,
                    this._FixFileName,
                    this.GetDateTimeTag(),
                    (this._FileIndex > 0) ? ("(" + this._FileIndex.ToString() + ")") : "",
                    ".",
                    this.FileExtension
                });
            }
        }


        /// <summary>
        /// 文件拓展名
        /// </summary>
        public string FileExtension
        {
            get
            {
                return this._FileNameExt;
            }
            set
            {
                if (value.CheckFileName(true))
                {
                    this._FileNameExt = value;
                    return;
                }
                throw new Exception("The file extension cannot contain the following characters: \\, /, :, *, ?, <, >, |");
            }
        }


        /// <summary>
        /// 文件路径
        /// </summary>
        public string Path { get; private set; }


        /// <summary>
        /// 子路径
        /// </summary>
        public string SubPath { get; private set; }


        /// <summary>
        /// 循环周期
        /// </summary>
        public TimeUnit CycleTime { get; private set; }

 
        /// <summary>
        /// 最大的文件大小
        /// </summary>
        public long MaxFileSize { get; private set; }

      
        /// <summary>
        /// 编码格式
        /// </summary>
        public Encoding Encoding { get; private set; }

       
        /// <summary>
        /// 是否忽略错误
        /// </summary>
        public bool IgnoreErrors { get; set; } = true;


        /// <summary>
        /// 是否是异步的
        /// </summary>
        public bool Async { get; set; } = true;


        private const int MEGABYTES = 1048576;


        /// <summary>
        /// 写buffer用的锁
        /// </summary>
        private readonly object _LockWriteBuffer = new object();


        /// <summary>
        /// 将缓冲区的内容写入到文件中用的锁
        /// </summary>
        private readonly object _LockBufferToFile = new object();


        /// <summary>
        /// 写入磁盘中用的锁
        /// </summary>
        private readonly object _LockWriteDiskFile = new object();


        /// <summary>
        /// 缓冲区
        /// </summary>
        private readonly StringBuilder[] _Buffers = new StringBuilder[]
        {
            new StringBuilder(),
            new StringBuilder()
        };


        /// <summary>
        /// 固定的文件名称
        /// </summary>
        private readonly string _FixFileName = "";


        /// <summary>
        /// 定时器   定时将txt写入到文件
        /// </summary>
        private readonly System.Timers.Timer _Timer;


        /// <summary>
        /// 写日志的后台任务
        /// </summary>
        private Task _Task;


        /// <summary>
        /// buffer索引
        /// </summary>
        private int _BufferIndex;


        /// <summary>
        /// 自动保存的时间
        /// </summary>
        private int _AutoSaveSeconds = 1;


        /// <summary>
        /// 文件索引
        /// </summary>
        private int _FileIndex;


        /// <summary>
        /// 文件拓展名 extension
        /// </summary>
        private string _FileNameExt = "log";


        /// <summary>
        /// 自动保存的时间
        /// </summary>
        public ushort AutoSaveSeconds
        {
            get
            {
                return (ushort)this._AutoSaveSeconds;
            }
            set
            {
                this._AutoSaveSeconds = (int)value;
                if (this._AutoSaveSeconds > 0)
                {
                    this._Timer.Interval = (double)(this._AutoSaveSeconds * 1000);
                    if (!this._Timer.Enabled)
                    {
                        this._Timer.Start();
                        return;
                    }
                }
                else
                {
                    this._Timer.Stop();
                }
            }
        }


        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="text">内容</param>
        /// <param name="mode"></param>
        public void Write(string text, WriteFileMode mode = WriteFileMode.Default)
        {
            object lockWriteBuffer = this._LockWriteBuffer;
            lock (lockWriteBuffer)
            {
                this._Buffers[this._BufferIndex].AppendLine(text);
                if (mode == WriteFileMode.RealTime)
                {
                    while (!this.WriteFile(false))
                    {
                        Thread.Sleep(100);
                    }
                }
                else if (mode == WriteFileMode.Async)
                {
                    this.WriteFile(true);
                }
                else
                {
                    this.WriteFile(this.Async);
                }
            }
        }


        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="text"></param>
        /// <param name="includeDateTime"></param>
        /// <param name="mode"></param>
        public void Write(string text, bool includeDateTime, WriteFileMode mode = WriteFileMode.Default)
        {
            if (includeDateTime)
            {
                this.Write(DateTime.Now.ToDateTimeString(false) + " " + text, mode);
                return;
            }
            this.Write(text, mode);
        }

      
        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="information"></param>
        /// <param name="mode"></param>
        public void Write(Information information, WriteFileMode mode = WriteFileMode.Default)
        {
            if (information != null)
            {
                this.Write(information.ToString(), mode);
                return;
            }
            if (this.IgnoreErrors)
            {
                return;
            }
            throw new ArgumentNullException("information");
        }


        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="informations"></param>
        /// <param name="mode"></param>
        public void Write(InformationCollection informations, WriteFileMode mode = WriteFileMode.Default)
        {
            if (informations != null)
            {
                this.Write(informations.ToString(), mode);
                return;
            }
            if (this.IgnoreErrors)
            {
                return;
            }
            throw new ArgumentNullException("informations");
        }


        /// <summary>
        /// 写入文件 
        /// </summary>
        /// <param name="async">是否是异步写文件</param>
        /// <returns></returns>
        private bool WriteFile(bool async)
        {
            object lockBuuferToFile = this._LockBufferToFile;
            lock (lockBuuferToFile)
            {
                if (this._Task == null || this._Task.IsCanceled || this._Task.IsCompleted || this._Task.IsFaulted)
                {
                    if (this._Buffers[this._BufferIndex].Length > 0)
                    {
                        //如果是异步的，开线程将当前缓冲区的内容先写入到文件
                        if (async)
                        {
                            int bufferIndex = this._BufferIndex;
                            this._BufferIndex ^= 1;
                            string text = this._Buffers[bufferIndex].ToString();
                            this._Buffers[bufferIndex].Clear();
                            this._Task = Task.Run(delegate ()
                            {
                                this.WriteBufferToLogFile(text);
                            });
                        }
                        else
                        {
                            //如果是同步的，则使用当前线程将内容写入到文件
                            string text2 = this._Buffers[this._BufferIndex].ToString();
                            this._Buffers[this._BufferIndex].Clear();
                            this.WriteBufferToLogFile(text2);
                        }
                    }
                    return true;
                }
            }
            return false;
        }


        /// <summary>
        /// 将buffer缓存中的数据写入到文件中(刷盘)
        /// </summary>
        /// <param name="text"></param>
        private void WriteBufferToLogFile(string text)
        {
            try
            {
                this.BuildFolder();
                this.CheckLogFileName();
                object lockWriteDiskFile = this._LockWriteDiskFile;
                lock (lockWriteDiskFile)
                {
                    using (FileStream fileStream = new FileStream(this.File, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
                    {
                        using (StreamWriter streamWriter = new StreamWriter(fileStream, Encoding.Unicode))
                        {
                            try
                            {
                                streamWriter.WriteAsync(text);
                            }
                            catch
                            {
                            }
                            finally
                            {
                                try
                                {
                                    streamWriter.Flush();
                                    streamWriter.Close();
                                }
                                catch
                                {
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (!this.IgnoreErrors)
                {
                    throw ex;
                }
            }
        }


        /// <summary>
        /// 创建文件夹
        /// </summary>
        private void BuildFolder()
        {
            if (!Directory.Exists(this.Path))
            {
                try
                {
                    Directory.CreateDirectory(this.Path);
                }
                catch (Exception ex)
                {
                    throw new Exception(string.Concat(new string[]
                    {
                        "Creation of subfolder '",
                        this.SubPath,
                        "' failed at path '",
                        this.Path,
                        "'. The error is:",
                        ex.Message
                    }));
                }
            }
        }


        /// <summary>
        /// 检查文件的名称
        /// </summary>
        private void CheckLogFileName()
        {
            if (this.MaxFileSize == 0L)
            {
                return;
            }
            while (System.IO.File.Exists(this.File))
            {
                try
                {
                    FileInfo fileInfo = new FileInfo(this.File);
                    if (fileInfo.Length < this.MaxFileSize)
                    {
                        break;
                    }
                }
                catch
                {
                }
                this._FileIndex++;
            }
        }


        /// <summary>
        /// 获取时间戳
        /// </summary>
        /// <returns></returns>
        private string GetDateTimeTag()
        {
            switch (this.CycleTime)
            {
                case TimeUnit.Second:
                    return DateTime.Now.ToString("yyyyMMddHHmmss");
                case TimeUnit.Minute:
                    return DateTime.Now.ToString("yyyyMMddHHmm");
                case TimeUnit.Hour:
                    return DateTime.Now.ToString("yyyyMMddHH");
                case TimeUnit.Day:
                    return DateTime.Now.ToString("yyyyMMdd");
                case TimeUnit.Week:
                    {
                        Week week = new Week(DateTime.Now);
                        return string.Format("{0:0000}{1:00}", week.Year, week.Index);
                    }
                case TimeUnit.Month:
                    return DateTime.Now.ToString("yyyyMM");
                case TimeUnit.Quarter:
                    {
                        DateTime now = DateTime.Now;
                        int num = (now.Month - 1) / 3 + 1;
                        return string.Format("{0:0000}{1}", now.Year, num);
                    }
                case TimeUnit.Year:
                    return DateTime.Now.ToString("yyyy");
                case TimeUnit.Era:
                    return string.Format("{0:0000}", DateTime.Now.Year);
                default:
                    return "";
            }
        }


        /// <summary>
        /// 定时写文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            this.WriteFile(this.Async);
        }

   

    }


}
